package com.alan.api.core;

import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 * @author Alan
 * @create 2018-01-10 21:17
 * @desc api大仓库
 **/
public class ApiStore {

    private ApplicationContext applicationContext;
    // API 接口存放的地方
    private HashMap<String,ApiRunnable> apiMap = new HashMap<String, ApiRunnable>();

    // spring ioc
    public ApiStore(ApplicationContext applicationContext) {
        Assert.notNull(applicationContext);
        this.applicationContext = applicationContext;
    }

    /**
     * 基于Spring IOC 容器里面的Bean查找对应的API方法 装载
     */
    public void loadApiFromSpringBeans(){
        // ioc 所以Bean
        // spring ioc 扫描
        String[] names = applicationContext.getBeanDefinitionNames();
        Class<?> type;
        for (String name : names) {
            type = applicationContext.getType(name);
            for (Method m :type.getDeclaredMethods()) {
                // 找到有 APIMapping 的Bean 把方法加入到apiStore中
                APIMapping apiMapping = m.getAnnotation(APIMapping.class);
                if(apiMapping != null){
                    addApiItem(apiMapping,name,m);
                }
            }
        }
    }

    private void addApiItem(APIMapping apiMapping, String beanName, Method method){
        // 验证接口模型规范
        for (Field field:method.getReturnType().getDeclaredFields()) {
            if(field.getDeclaringClass().equals(Object.class)){
                throw new RuntimeException(String.format("%s,%s 不符合接口规范",
                        method.getDeclaringClass(),
                        method.getName()));
            }
        }
        // 添加执行器
        ApiRunnable apiRun = new ApiRunnable();
        // apiName
        apiRun.apiName = apiMapping.value();
        // apiName 对应的目标方法
        apiRun.targetMethod = method;
        // 对应的spring ioc 中的beanName
        apiRun.targetName = beanName;
        // 进行保存
        apiMap.put(apiMapping.value(),apiRun);
    }

    /**
     * 通过 apiName 得到 执行器
     * @param apiName
     * @return
     */
    public ApiRunnable findApiRunnable(String apiName){
        return apiMap.get(apiName);
    }

    public ApiRunnable findApiRunnable(String apiName,String version){
        return apiMap.get(apiName+"_"+version);
    }

    public class ApiRunnable {
        // alan.alan.user.getUser
        public String apiName;
        // ioc bean 目标方法
        public Method targetMethod;
        // ioc bean 名称
        public String targetName;
        // ioc bean 实例
        public Object target;

        public Object run(Object... args) throws InvocationTargetException, IllegalAccessException {
            // 多线程安全问题
            if(target == null){
                target = applicationContext.getBean(targetName);
            }
            return targetMethod.invoke(target,args);
        }

        public Class<?>[] getParamTypes(){
            return targetMethod.getParameterTypes();
        }

        public String getApiName() {
            return apiName;
        }

        public void setApiName(String apiName) {
            this.apiName = apiName;
        }

        public Method getTargetMethod() {
            return targetMethod;
        }

        public void setTargetMethod(Method targetMethod) {
            this.targetMethod = targetMethod;
        }

        public String getTargetName() {
            return targetName;
        }

        public void setTargetName(String targetName) {
            this.targetName = targetName;
        }

        public Object getTarget() {
            return target;
        }

        public void setTarget(Object target) {
            this.target = target;
        }
    }

}
